package bll

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/lib/pq"
	"gorm.io/gorm"
	log "ota/collector/logger"
	"ota/config"
	"ota/errors"
	"ota/event"
	"ota/model"
	"ota/model/entity"
	"ota/model/mapping"
	"ota/store"
	"ota/store/postgres"
	"time"
)

type firmwareType struct {
	iFirmwareType store.IFirmwareType
}

var FirmwareType = &firmwareType{
	iFirmwareType: postgres.FirmwareType,
}

func init() {
	Register(FirmwareType)
}

func (a *firmwareType) init() func() {
	go a.initTableData()
	return func() {}
}

func (a *firmwareType) initTableData() {

	var (
		ctx                     = context.Background()
		firmwareTypeSettingsMap = config.Conf.FirmwareTypeSettings.ToMap1()
	)
	_, list, err := a.iFirmwareType.List(ctx, &model.FirmwareTypeListRequest{Index: 1, Size: -1}, model.QueryOptions{OnlyList: true})
	if err != nil {
		log.Errorf("list firmware type: %v", err)
		return
	}

	var (
		deleteList []*model.FirmwareTypeDeleteRequest
		list2Map   = make(map[string]*entity.FirmwareType)
		updateList []*model.FirmwareTypeUpdateRequest
		createList []*entity.FirmwareType
	)
	for _, l := range list {
		list2Map[l.Name] = l
	}

	// get deleteList and updateList
	for _, l := range list2Map {
		if v, ok := firmwareTypeSettingsMap[l.Name]; !ok {
			deleteList = append(deleteList, &model.FirmwareTypeDeleteRequest{Id: l.Id})
		} else {
			if v.DisplayName == l.Name && v.DisplayDescription == l.Description {
				continue
			} else {
				updateList = append(updateList, &model.FirmwareTypeUpdateRequest{Id: l.Id, Name: &v.DisplayName, Description: &v.DisplayDescription})
			}
		}
	}

	// get createList
	for v, k := range firmwareTypeSettingsMap {
		if l, ok := list2Map[v]; ok {
			if k.DisplayName == l.Name && k.DisplayDescription == l.Description {
				continue
			}
		} else {
			createList = append(createList, &entity.FirmwareType{Name: k.DisplayName, Description: k.DisplayDescription})
		}
	}

	if len(deleteList) > 0 {
		for _, id := range deleteList {
			if err = a.Delete(ctx, id); err != nil {
				log.Errorf("firmwareType init error(delete firmwareType:%+v error): %v", id, err)
			}
		}
	}
	if len(createList) > 0 {
		for _, id := range createList {
			if _, err = a.iFirmwareType.Create(ctx, id); err != nil {
				log.Errorf("firmwareType init error(create firmwareType error): %v", err)
			}
		}
	}

	if len(updateList) > 0 {
		for _, id := range updateList {
			if err = a.Update(ctx, id); err != nil {
				log.Errorf("firmwareType init error(update firmwareType error): %v", err)
			}
		}
	}

	log.Infof("firmwareType init complete success,firmware type(from db:%+v, from config:%+v)", func() string {
		b1, _ := json.Marshal(list)
		return string(b1)
	}(), func() string {
		b2, _ := json.Marshal(firmwareTypeSettingsMap)
		return string(b2)
	}())
	return
}

func (a *firmwareType) onEvent(*event.Data) {}

func (a *firmwareType) checkExist(ctx context.Context, name string) bool {

	// 车型名称校验
	var count int64
	postgres.GetDB(ctx).Model(&entity.FirmwareType{}).Where("name = ?", name).Count(&count)
	if count > 0 {
		return true
	}

	return false
}

// Create
func (a *firmwareType) Create(ctx context.Context, in *model.FirmwareTypeCreateRequest) error {

	if b := a.checkExist(ctx, in.Name); b {
		return fmt.Errorf("固件类型名称已存在")
	}

	var (
		err error
	)
	c := buildFirmwareType(in)
	_, err = a.iFirmwareType.Create(ctx, c)
	return err
}

// Update
func (a *firmwareType) Update(ctx context.Context, in *model.FirmwareTypeUpdateRequest) error {

	//if b := a.checkExist(ctx, *in.Name); b {
	//	return fmt.Errorf("固件类型名称已存在")
	//}

	var (
		dict = make(map[string]interface{})
	)

	if in.Name != nil {
		dict["name"] = in.Name
	}

	if in.Description != nil {
		dict["description"] = in.Description
	}

	if in.UpdatedAt != nil {
		dict["updated_at"] = in.UpdatedAt
	}

	// do other update here
	updateAt := time.Now().Unix()
	in.UpdatedAt = &updateAt
	return a.iFirmwareType.Update(ctx, in.Id, dict)
}

func (a *firmwareType) check(ctx context.Context, id int64, op model.OtaFirmwareTypeOpType) error {

	respFirmware, err := a.iFirmwareType.Find(ctx, &model.FirmwareTypeInfoRequest{Id: id})
	if err != nil {
		log.Errorf("get firmware type err: %v", err)
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.InternalServerError.Error()
		}
	}
	if respFirmware.Id == 0 {
		return errors.FirmwareTypeNotExist.Error()
	}
	if _, data, err := Firmware.iFirmware.List(ctx, &model.FirmwareListRequest{
		Index:   1,
		Size:    1,
		TypeIds: pq.Int64Array{id},
	}, model.QueryOptions{
		OnlyList: true,
	}); err != nil {
		log.Errorf("get upgrade plan err: %v", err)
		return errors.InternalServerError.Error()
	} else {
		if len(data) > 0 {
			if op == model.FirmwareTypeOpDelete {
				return fmt.Errorf("已有固件(%v %v)关联该固件类型，不允许删除", data[0].Id, data[0].Name)
			}
		}
	}

	return nil
}

/*
删除：已有数据关联该固件类型，不允许删除
点击删除按钮，报错提示：“已有数据关联该固件类型，不允许删除”
如没有数据关联，支持删除
*/
// Delete
func (a *firmwareType) Delete(ctx context.Context, in *model.FirmwareTypeDeleteRequest) error {

	if err := a.check(ctx, in.Id, model.FirmwareTypeOpDelete); err != nil {
		return err
	}

	/* TODO */
	return a.iFirmwareType.Delete(ctx, in.Id)
}

// List
func (a *firmwareType) List(ctx context.Context, in *model.FirmwareTypeListRequest) (*model.FirmwareTypeListResponse, error) {
	var (
		err   error
		total int
		list  []*entity.FirmwareType
		out   = &model.FirmwareTypeListResponse{}
		opts  = model.QueryOptions{OrderFields: model.NewOrderFields(model.NewOrderField("created_at", model.OrderByDESC))}
	)

	if total, list, err = a.iFirmwareType.List(ctx, in, opts); err != nil {
		return nil, err
	}

	out.Total = total
	out.List = mapping.FirmwareTypesEntityToDto(list)

	return out, nil
}

// Find
func (a *firmwareType) Find(ctx context.Context, in *model.FirmwareTypeInfoRequest) (*model.FirmwareTypeInfo, error) {
	var (
		err  error
		data *entity.FirmwareType
		out  = &model.FirmwareTypeInfo{}
	)

	if data, err = a.iFirmwareType.Find(ctx, in); err != nil {
		return nil, err
	}

	out = mapping.FirmwareTypeEntityToDto(data)
	return out, nil
}

// buildFirmwareType build entity
func buildFirmwareType(in *model.FirmwareTypeCreateRequest) *entity.FirmwareType {

	ety := &entity.FirmwareType{}

	ety.Name = in.Name

	ety.Description = in.Description

	ety.CreatedAt = time.Now().Unix()

	return ety
}
